package com.xiyu.service.service.market;

import cn.hutool.core.util.StrUtil;
import com.xiyu.service.enums.mailbox.mail.MailboxMailResourceTypeEnum;
import com.xiyu.service.enums.market.task.MarketTaskStatusEnum;
import com.xiyu.service.framework.mail.service.BatchSendMailService;
import com.xiyu.service.model.infra.file.InfraFile;
import com.xiyu.service.model.market.task.*;
import com.xiyu.service.model.system.user.SystemUser;
import com.xiyu.service.repository.infra.file.InfraFileRepository;
import com.xiyu.service.service.infra.data.DictNoService;
import com.xiyu.service.service.system.user.UserService;
import com.xiyu.service.vo.market.marketTask.MarketTaskSingleGetOutput;
import com.xiyu.service.repository.market.task.MarketTaskReceiverRepository;
import com.xiyu.service.repository.market.task.MarketTaskContentRepository;
import com.xiyu.service.repository.market.task.MarketTaskResourceRepository;
import com.xiyu.service.vo.market.marketTask.MarketTaskUpdateInput;
import com.xiyu.service.vo.market.marketTask.MarketTaskCreateInput;
import com.xiyu.service.repository.market.task.MarketTaskRepository;
import com.xiyu.service.vo.market.marketTask.MarketTaskPageQueryOutput;
import com.xiyu.service.vo.market.marketTask.MarketTaskPageQueryInput;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;

import java.time.LocalDateTime;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.babyfish.jimmer.DraftObjects;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.market.MarketTaskConvert;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.market.MarketTaskErrorCode.*;
import static com.xiyu.service.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.xiyu.service.util.mail.MailUtils.extractImagePaths;

/**
 * 邮件营销 Service 实现类
 */
@Service
@Validated
public class MarketTaskServiceImpl implements MarketTaskService {

    @Resource
    private MarketTaskReceiverRepository marketTaskReceiverRepository;

    @Resource
    private MarketTaskContentRepository marketTaskContentRepository;

    @Resource
    private MarketTaskResourceRepository marketTaskResourceRepository;

    @Resource
    private MarketTaskRepository marketTaskRepository;

    @Resource
    private UserService userService;

    @Resource
    private DictNoService dictNoService;

    @Resource
    private InfraFileRepository infraFileRepository;

    @Resource
    private BatchSendMailService batchSendMailService;



    @Override
    public PageResult<MarketTaskPageQueryOutput> pageQuery(MarketTaskPageQueryInput inputVO) {
        List<Long> ownerUserIds = userService.getOwnerUserIds(getLoginUserId());
        Page<MarketTask> pageMarketTask = marketTaskRepository.pageQuery(inputVO, ownerUserIds);

        List<MarketTaskPageQueryOutput> listMarketTask = MarketTaskConvert.INSTANCE.pageQueryOutputConvert(pageMarketTask.getRows());
        return new PageResult<>(listMarketTask, pageMarketTask.getTotalRowCount());
    }

    public void validateTask(MarketTask marketTask){
        if(marketTask.isScheduled()){
            if(marketTask.sendTime() == null)
                throw exception(MARKET_TASK_SEND_TIME_NOT_EMPTY);
            if(marketTask.sendTime().isBefore(LocalDateTime.now())){
                throw exception(MARKET_TASK_SEND_TIME_IS_AFTER_NOW);
            }
        }

        if(marketTask.name().isEmpty()){
            throw exception(MARKET_TASK_NAME_NOT_EMPTY);
        }
    }

    private String execTask(Long id) {
        MarketTask task = marketTaskRepository.findDetailById(id).orElse(null);
        if(task == null)
            return "";
        if(!task.status().equals(MarketTaskStatusEnum.UNEXECUTED.getValue()))
            return "";
        if(task.isScheduled()){
            if(task.sendTime() == null)
                return "";
            if(task.sendTime().isAfter(LocalDateTime.now())){
                return "";
            }
        }
        try {
            return batchSendMailService.marketTaskExec(task);
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(MarketTaskCreateInput inputVO) {
        MarketTask newMarketTask = MarketTaskConvert.INSTANCE.createInputConvert(inputVO);
        validateTask(newMarketTask);
        // 查找邮件图片资源
        ArrayList<String> imagePaths = extractImagePaths(Objects.requireNonNull(newMarketTask.content()).content());
        List<InfraFile> files = infraFileRepository.findByUrls(imagePaths);
        List<MarketTaskResource> inlineResource = new ArrayList<>();
        for(InfraFile file : files){
            inlineResource.add(MarketTaskResourceDraft.$.produce(draft ->
                            draft.setResourceType(MailboxMailResourceTypeEnum.INLINE.getValue())
                                    .setFileName(file.name()).setUrlName(file.url()) .setSize(file.size())
                    )
            );
        }
        MarketTask finalNewMarketTask = newMarketTask;
        newMarketTask = MarketTaskDraft.$.produce(newMarketTask, draft ->
                draft
                        .setTaskNo(dictNoService.produceNo("marketTask"))
                        .setResources(Stream.concat(finalNewMarketTask.resources().stream(), inlineResource.stream()).collect(Collectors.toList()))
                        .setContent(MarketTaskContentDraft.$.produce(contentDraft -> contentDraft.setContent(inputVO.getContent().getContent())))
                        .setRequestCount(0).setClickCount(0).setOpenCount(0).setDeliverCount(0).setReplyCount(0).setInvalidCount(0)
        );
        newMarketTask = marketTaskRepository.insert(newMarketTask);

        String errorMessage = execTask(newMarketTask.id());
        if(!errorMessage.isEmpty()){
            throw exception(MARKET_TASK_SEND_ERROR, errorMessage);
        }
        return newMarketTask.id();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(MarketTaskUpdateInput inputVO) {
        Optional<MarketTask> optionalOldMarketTask = marketTaskRepository.findById(inputVO.getId());
        if(optionalOldMarketTask.isEmpty()){
            throw exception(MARKET_TASK_NOT_EXIST);
        }

        MarketTask updateMarketTask = MarketTaskConvert.INSTANCE.updateInputConvert(inputVO);
        if(!updateMarketTask.status().equals(MarketTaskStatusEnum.DRAFT.getValue()) && !updateMarketTask.status().equals(MarketTaskStatusEnum.UNEXECUTED.getValue())){
            throw exception(MARKET_TASK_DRAFT_CAN_EDIT);
        }
        validateTask(updateMarketTask);
        
        // 查找邮件图片资源
        ArrayList<String> imagePaths = extractImagePaths(Objects.requireNonNull(updateMarketTask.content()).content());
        List<InfraFile> files = infraFileRepository.findByUrls(imagePaths);
        List<MarketTaskResource> inlineResource = new ArrayList<>();
        for(InfraFile file : files){
            inlineResource.add(MarketTaskResourceDraft.$.produce(draft ->
                            draft.setResourceType(MailboxMailResourceTypeEnum.INLINE.getValue())
                                    .setFileName(file.name()).setUrlName(file.url())
                    )
            );
        }
        // 更新附件
        updateMailAttachmentResource(updateMarketTask.resources(), inputVO.getId());
        updateMailInlineResource(inlineResource, inputVO.getId());

        // 更新收件人
        updateTaskReceiver(updateMarketTask.receivers(), inputVO.getId());

        updateMarketTask = MarketTaskDraft.$.produce(updateMarketTask, draft -> {
            DraftObjects.unload(draft, MarketTaskProps.RESOURCES);
            DraftObjects.unload(draft, MarketTaskProps.RECEIVERS);
        });

        marketTaskRepository.update(updateMarketTask);

        String errorMessage = execTask(updateMarketTask.id());
        if(!errorMessage.isEmpty()){
            throw exception(MARKET_TASK_SEND_ERROR, errorMessage);
        }
        return true;
    }

    private void updateTaskReceiver(List<MarketTaskReceiver> newReceivers, Long taskId){
        List<MarketTaskReceiver> oldReceivers = marketTaskReceiverRepository.findByTaskId(taskId);
        for(MarketTaskReceiver newReceiver : newReceivers){
            if(oldReceivers.stream().noneMatch(oldReceiver -> oldReceiver.mailAddress().equals(newReceiver.mailAddress()))){
                marketTaskReceiverRepository.insert(MarketTaskReceiverDraft.$.produce(newReceiver, draft -> draft.setTaskId(taskId)));
            }
        }
        for(MarketTaskReceiver oldReceiver : oldReceivers){
            if(newReceivers.stream().noneMatch(newReceiver -> newReceiver.mailAddress().equals(oldReceiver.mailAddress()))){
                marketTaskReceiverRepository.deleteById(oldReceiver.id());
            }
        }
    }

    private void updateMailInlineResource(List<MarketTaskResource> newResources, Long taskId){
        List<MarketTaskResource> oldResources = marketTaskResourceRepository.findByTaskIdAndResourceType(taskId, MailboxMailResourceTypeEnum.INLINE.getValue());
        if(oldResources.isEmpty() && newResources.isEmpty())
            return;
        if(newResources.isEmpty()){
            marketTaskResourceRepository.deleteByIds(oldResources.stream().map(MarketTaskResource::id).collect(Collectors.toList()));
            return;
        }
        for(MarketTaskResource newResource : newResources){
            if(oldResources.stream().noneMatch(oldResource -> oldResource.urlName().equals(newResource.urlName()))){
                marketTaskResourceRepository.insert(MarketTaskResourceDraft.$.produce(newResource, draft -> draft.setTaskId(taskId)));
            }
        }
        for(MarketTaskResource oldResource : oldResources){
            if(newResources.stream().noneMatch(newResource -> newResource.urlName().equals(oldResource.urlName()))){
                marketTaskResourceRepository.deleteById(oldResource.id());
            }
        }
    }

    private void updateMailAttachmentResource(List<MarketTaskResource> newResources, Long templateId){
        List<MarketTaskResource> oldResources = marketTaskResourceRepository.findByTaskIdAndResourceType(templateId, MailboxMailResourceTypeEnum.ATTACHMENT.getValue());
        if(oldResources.isEmpty() && newResources.isEmpty())
            return;
        if(newResources.isEmpty()){
            marketTaskResourceRepository.deleteByIds(oldResources.stream().map(MarketTaskResource::id).collect(Collectors.toList()));
            return;
        }
        if(oldResources.isEmpty()){
            for(MarketTaskResource newResource : newResources){
                marketTaskResourceRepository.insert(
                        MarketTaskResourceDraft.$.produce(newResource, draft -> draft.setTaskId(templateId))
                );
            }
            return;
        }
        int index = 0, diffIndex = -1;
        for(;index < oldResources.size(); index ++){
            if(newResources.size() <= index
                    || (!newResources.get(index).fileName().equals(oldResources.get(index).fileName()) && !newResources.get(index).urlName().equals(oldResources.get(index).urlName()))){
                if(diffIndex == -1){
                    diffIndex = index;
                }
                marketTaskResourceRepository.deleteById(oldResources.get(index).id());
            }
        }
        if(diffIndex == -1){
            diffIndex = index;
        }
        for(;diffIndex < newResources.size(); diffIndex++){
            marketTaskResourceRepository.insert(
                    MarketTaskResourceDraft.$.produce(newResources.get(diffIndex), draft -> draft.setTaskId(templateId))
            );
        }
    }

    @Override
    public MarketTaskSingleGetOutput singleGet(Long id) {
        Optional<MarketTask> optionMarketTask = marketTaskRepository.findDetailById(id);
        if(optionMarketTask.isEmpty()){
            throw exception(MARKET_TASK_NOT_EXIST);
        }
        return MarketTaskConvert.INSTANCE.singleGetOutputConvert(optionMarketTask.get());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleted(Long id) {
        MarketTask marketTask = marketTaskRepository.findNullable(id);
        if(marketTask == null){
            throw exception(MARKET_TASK_NOT_EXIST);
        }
        if(!Objects.equals(marketTask.status(), MarketTaskStatusEnum.CANCEL.getValue())
                && !Objects.equals(marketTask.status(), MarketTaskStatusEnum.DRAFT.getValue())
                && !Objects.equals(marketTask.status(), MarketTaskStatusEnum.UNEXECUTED.getValue())){
            throw exception(MARKET_TASK_STATUS_NOT_DELETE);
        }

        marketTaskRepository.deleteById(id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancel(Long id) {
        MarketTask marketTask = marketTaskRepository.findNullable(id);
        if(marketTask == null){
            throw exception(MARKET_TASK_NOT_EXIST);
        }
        if(!Objects.equals(marketTask.status(), MarketTaskStatusEnum.DRAFT.getValue())
                && !Objects.equals(marketTask.status(), MarketTaskStatusEnum.UNEXECUTED.getValue())){
            throw exception(MARKET_TASK_STATUS_NOT_CANCEL);
        }
        marketTaskRepository.updateStatus(id, MarketTaskStatusEnum.UNEXECUTED.getValue());
        return true;
    }

    @Override
    public void taskJobExec() {
        List<Long> taskIds = marketTaskRepository.findScheduledTask();
        for(Long taskId : taskIds){
           String errorMessage = execTask(taskId);
           if(!errorMessage.isEmpty()){
               taskError(taskId, errorMessage);
           }
        }
    }

    private void taskError(Long id, String errorMessage){
        marketTaskRepository.updateErrorMessage(id, StrUtil.sub(errorMessage, 0, 1024));
        marketTaskRepository.updateStatus(id, MarketTaskStatusEnum.FAIL.getValue());
    }



}
